package client;


import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.concurrent.Semaphore;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import parameters.RLM_Parameters;
import utils.Constants;
import utils.MessageViewer;
import utils.NICManagerRLM;
import utils.NetworkUtils;
import utils.RSA_Password;
import client_gui.ClientWindow;
import client_gui.OptionsDialog;
import client_server.ClientServer;
import client_tree.ClientModel;


/**
 * Klasa koja predstavlja Client aplikaciju.
 * Sadrzi main() metod za pokretanje klijent aplikacije.
 * 
 * @author UrosKrkic & Radivoje Popovic
 */
public class ClientApplication implements Constants, ApplicationInterface {

	/**
	 * Tabela koja cuva aktivne client-to-client konekcije.
	 */
	public static Hashtable<String, ClientBindObject> clientBinding = new Hashtable<String, ClientBindObject>();
	
	
	public static Semaphore bindingSemaphore = new Semaphore(1, true);
	
	public static Semaphore loginSemaphore = new Semaphore(1, true);
	
	public static Semaphore clientServerSemaphore = new Semaphore(1, true);
	
	public static Semaphore dialogSemaphore = new Semaphore(1, true);
	
	public static Semaphore callRequestSemaphore = new Semaphore(1, true);
	
	/**
	 * Semafor obezbedjuje da se izvrsavanje createSocketConnection metode stopira, dok se
	 * ne zavrsi multicast mehanizam. Sve ovo je pod pretpostavkom da se u prvom pokusaju
	 * kreiranja konekcije, konekcija nije kreirala, odnosno, uhvacen je izuzetak.
	 */
	public static Semaphore multicastPrecednceSemaphore = new Semaphore(1, true);
	
	
	public static Semaphore streaminAcceptSemaphore = new Semaphore(1, true);
	
	
	/**
	 * Socket izmedju servera i klijenta.
	 */
	private Socket socket;

	
	/**
	 * Konekcija dodeljena socket-u izmedju servera i klijenta.
	 */
	private ClientSocketConnection connection = null;
	
	
	/**
	 * Objekat koji predstavlja logovanog korisnika.
	 */
	private ClientUser clientUser;
	
		
	/**
	 * IP adresa serverskog procesa na strani klijenta.
	 */
//	private String clientSideServerIP;
	
	/**
	 * Port serverskog procesa na strani klijenta
	 */
//	private int clientSideServerPort;
	
	
	/**
	 * Objekat koji predstavlja thread (nit) serverskog procesa na strani klijenta.
	 */
	private ClientServer clientServer = null;
	
	
	/**
	 * Prozor aplikacije.
	 */
	private ClientWindow window;
	
	
	/**
	 * Model aplikacije.
	 */
	private ClientModel clientModel;
	
	
	/**
	 * Stanje aplikacije.
	 */
	private int state = INITIAL_STATE;
	


	
	/**
	 * Mutlicast IP adressa - grupa.
	 */
	private InetAddress multicastIPAddress = null;
	
	
	/**
	 * Multicast port.
	 */
	private int multicastPort = 10001;
	
	
	
	/**
	 * Flag koji oznacava multicast mehanizam (thread) prekinut.
	 */
	private boolean multicastInterruped = false;
	
	
	/**
	 * Objekat koji predstavlja multicast mehanizam (thread).
	 */
	private ClientMulticast multicastRunner = null;
	
	
	/**
	 * Flag koji oznacava da li je primljen odgovor na multicast poruku.
	 */
	public static boolean multicastReceived = false;

	
	// added from E...
	private static final String		lockFileName	= "rlm.lock";
	private File					lockFile		= null;
	private FileLock				lock			= null;
	
	private static HashMap<String,String> cmdLineParameters = new HashMap<String,String>();
	
	public static enum CmdLineParams { auto_started };
	
	public static enum Platform { Undefined, Windows, Non_Windows };
	
	private static Platform platform;
	
	private static boolean auto_started = false;
	
	private static String			version			= "0.0";
	
	
	/**
	 * Konstruktor.
	 * Kreira model aplikacije, kreira prozor aplikacije, i prikazuje prozor aplikacije.
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientApplication() {
		
		getRLMClientMulticastParameters();
		createApplicationDataFolder();
		
		if ( acquireLock() == false ) {
			alreadyRun();
			System.exit(1);
		}

		loadVersion();
		
		ShutdownHook hook = new ShutdownHook(this);
		
		Runtime.getRuntime().addShutdownHook(hook);
		
		RLM_Parameters.loadParameters();
		
		NICManagerRLM.readAdapters();
		
		if ( platform == Platform.Windows )
			RLMAutostart.checkAutoStartup();
		
		clientModel = new ClientModel(this);
		window = new ClientWindow(this);
		
		setLoadedParameters();
		
		if ( RLM_Parameters.isFirstStart() ) {
			RLM_Parameters.setFirstStart(false);
			
			OptionsDialog settingsDialog = new OptionsDialog(window);
			settingsDialog.setVisible(true);
		}

		
		try {
			clientServerSemaphore.acquire();
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		if ( !isAutoStarted() )
			window.setVisible(true);
		
		window.showSplashScreen();
	}
	
	
	
	/**
	 * Metod ucitava parametre RLM klijenta iz konfiguracionog file-a.
	 * @author UrosKrkic & Radivoje Popovic
	 */
	private void getRLMClientMulticastParameters() {
		
		ResourceBundle bundle = null;
		
		try {
			bundle = PropertyResourceBundle.getBundle("RLMClientConfig");
		}
		catch (Exception e) {
			try {
				multicastIPAddress = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
			}
			catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
			multicastPort = DEFAULT_MULTICAST_PORT;
			
			return;
		}
	
		try {
			String multicastAddress = bundle.getString("MULTICAST_IP");
			
			multicastIPAddress = InetAddress.getByName(multicastAddress);
			
			if ( !multicastIPAddress.isMulticastAddress() ) {
				throw new Exception("Not multicast address!");
			}
		}
		catch (Exception e) {
			try {
				multicastIPAddress = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
			}
			catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
		}
		
		try {
			multicastPort = Integer.parseInt( bundle.getString("MULTICAST_PORT") );
		}
		catch (Exception e) {
			multicastPort = DEFAULT_MULTICAST_PORT;
		}
	}


	
	
	/**
	 * Metod setuje referencu na objekat koji predstavlja serverski proces na strani klijenta.
	 * @param clientServer - serverski proces na strani klijenta
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public void setClientServer(ClientServer clientServer) {
		this.clientServer = clientServer;
	}
	
	
	/**
	 * Metod vraca multicast IP adresu (grupu).
	 * @return - multicast adresa
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public InetAddress getMulticastIPAddress() {
		return this.multicastIPAddress;
	}
	
	
	/**
	 * Metod vraca multicast port.
	 * @return - multicast port
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public int getMulticastPort() {
		return this.multicastPort;
	}
	
	
	/**
	 * Metod stopira multicast mehanizam (thread).
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public void stopMulticasting() {
		multicastInterruped = true;
		
		this.multicastRunner.interrupt();
		
		if (multicastReceived == false) {
			multicastPrecednceSemaphore.release();
		}
	}

	
	
	/**
	 * Metod kreira novi socket i konekciju ka serveru, i setuje IP adresu serverskog
	 * procesa klijenta. Sve ovo se desava ako je connection == null, sto znaci da
	 * ne postoji vec kreirana konekcija. Ukoliko su parametri socket-a neispravni,
	 * pokusava se sa pronalazenjem servera, multicast mehanizmom.
	 * U tom slucaju, posto se u provom pokusaju kreiranja konekcije multicast semafor
	 * zakljucao, to prouzrokuje da ce se izvrsavanje metode stopirati dok se ne zavrsi
	 * multicast mehanizam (usespno ili neuspesno - pod uspehom se smatra odgovor na
	 * multicast poruku), jer se iz ClientMulticast threada oslobadja multicast semafor.
	 * 
	 * Ukoliko je multicast thread prekinut (multicastInterrupted == true) i ukoliko je
	 * primljen odgovor na multicast poruku (multicastReceived == true), inicira se nova
	 * konekcija ka RLM serveru. Ukoliko se i tad ne kreira konekcija, javlja se poruka
	 * o gresci.
	 * Ukoliko nije primljen odgovor na multicast poruku, a multicast thread je prekinut,
	 * takodje se javalja poruka o gresci.
	 * 
	 * Ukoliko se socket kreira, nastavlja se sa izvrsavanjem metode i kreiranje
	 * ClientSocketConnection objekta.
	 * 
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public boolean createSocketConnection() {
		
		InetAddress address = null;
		
		try {
			address = InetAddress.getByName(RLM_Parameters.getNetworkInterfaceToConnect());
			
			if (address == null) {
//				address = NetworkUtils.obtainLocalAdress();
				address = NetworkUtils.getLocalAddress();
			}
		}
		catch (UnknownHostException e3) {
			if (address == null) {
//				address = NetworkUtils.obtainLocalAdress();
				address = NetworkUtils.getLocalAddress();
			}
		}
		
		if (connection == null) {
			try {
				multicastPrecednceSemaphore.acquire();
				window.setCursor(new Cursor(Cursor.WAIT_CURSOR));
				if (address != null)
					socket = new Socket(RLM_Parameters.getServerName(), RLM_Parameters.getServerPort(), address, 0);
				else
					socket = new Socket(RLM_Parameters.getServerName(), RLM_Parameters.getServerPort());
			}
			catch (Exception e) {
				e.printStackTrace();
				multicastRunner = new ClientMulticast(this);
				
				try {
					multicastPrecednceSemaphore.acquire();
				}
				catch (InterruptedException e2) {
					e2.printStackTrace();
				}
				
				if (multicastInterruped == true && multicastReceived == true) {
					try {
						if (address != null)
							socket = new Socket(RLM_Parameters.getServerName(), RLM_Parameters.getServerPort(), address, 0);
						else
							socket = new Socket(RLM_Parameters.getServerName(), RLM_Parameters.getServerPort());
					}
					catch (Exception e1) {
						MessageViewer.showError(window, "Bad SERVER Address or Port number, or SERVER does not respond!");
						window.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
						ClientApplication.multicastPrecednceSemaphore.release();
						return false;
					}
				}
				else {
					MessageViewer.showError(window, "Bad SERVER Address or Port number, or SERVER does not respond!");
					window.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
					ClientApplication.multicastPrecednceSemaphore.release();
					return false;
				}
			}
			
			connection = new ClientSocketConnection(this, socket, CLIENT_SERVER_CONNECTION);
//			setClientSideServerIP(connection.getServerAddress());
			
			window.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			ClientApplication.multicastPrecednceSemaphore.release();
			
			return true;
		}
		
		window.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		ClientApplication.multicastPrecednceSemaphore.release();
		
		return true;
	}
	
	
	
	/**
	 * Metod vraca referencu na konekciju pridruzenu socketu izmedju servera i klijenta.
	 * @return - konekcija pridruzena ovo socketu
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientSocketConnection retrieveSocketConnection() {
		return this.connection;
	}
	
	
	
	/**
	 * Metod setuje konekciju prema serveru.
	 * @param connection - konekcija prema serveru
	 */
	public void setSocketConnection(ClientSocketConnection connection) {
		this.connection = connection;
	}
	
	
	
	/**
	 * Metod vraca referencu na model klijent aplikacije.
	 * @return model klijent aplikacije
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientModel getClientModel() {
		return this.clientModel;
	}
	
	
	/**
	 * Metod vraca stanje klijent aplikacije.
	 * @return stanje klijent aplikacije
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public int getState() {
		return this.state;
	}
	
	
	/**
	 * Metod setuje stanje klijent aplikacije.
	 * @param state - novo stanje klijent aplikacije
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public void setState(int state) {
		this.state = state;
	}
	

	/**
	 * Metod vraca referencu na prozor aplikacije.
	 * @return referenca na prozor aplikacije
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientWindow getWindow() {
		return this.window;
	}
	

	
	/**
	 * Metod vraca referencu na IP adresu serverskog procesa na strani klijenta.
	 * @return - IP adresa serverskog procesa na strani klijenta
	 * @author UrosKrkic & Radivoje Popovic
	 */
//	public String getClientSideServerIP() {
//		return this.clientSideServerIP;
//	}
	
	
	/**
	 * Metod vraca broj porta serverskog procesa na strani klijenta.
	 * @return - port serverskog procesa na strani klijenta
	 * @author UrosKrkic & Radivoje Popovic
	 */
//	public int getClientSideServerPort() {
//		return this.clientSideServerPort;
//	}
	
	
	/**
	 * Metod setuje IP adresu serverskog procesa na strani klijenta, nakon kreiranja konekcije sa
	 * serverom.
	 * @param address - IP adresa serverskog procesa na strani klijenta
	 * @author UrosKrkic & Radivoje Popovic
	 */
//	public void setClientSideServerIP(InetAddress address) {
//		
//		if (clientServer == null)
//			return;
//		
//		clientServer.setIPAddress(address);
// 
//		this.clientSideServerIP = clientServer.getIP_address();
//	}
	
	
	/**
	 * Metod setuje port serverskog procesa na strani klijenta.
	 * @param port - port koji se setuje
	 * @author UrosKrkic & Radivoje Popovic
	 */
//	public void setClientSideServerPort(int port) {
//		this.clientSideServerPort = port;
//	}
	
	
	/**
	 * Metod stopira serverski proces na strani klijenta.
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public void stopClientServer() {
		if (this.clientServer != null)
			this.clientServer.stopClientServer();
		
		this.clientServer = null;
	}
	
	
	/**
	 * Metod vraca referencu na logovanog korisnika.
	 * @return - referenca na logovanog korisnika
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientUser getClientUser() {
		return this.clientUser;
	}
	
	
	/**
	 * Metod setuje ClientUser-a na prosledjeni argument.
	 * @param clientUser - user koji koristi RLM Messenger
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public void setClientUser(ClientUser clientUser) {
		this.clientUser = clientUser;
	}
	
	
	/**
	 * Metod setuje ucitane parametre.
	 */
	private void setLoadedParameters() {
		RSA_Password rsa = new RSA_Password();
		
		int width = RLM_Parameters.getWidth();
		int height = RLM_Parameters.getHeight();
		
		if (width != -1 && height != -1) {
			window.setSize(width, height);
		}
		
		int x = RLM_Parameters.getLocation_x();
		int y = RLM_Parameters.getLocation_y();
		
		if (x != -1 && y != -1) {
			window.setLocation(x,y);
		}
		
		window.setAlwaysOnTop(RLM_Parameters.isAlwaysOnTop());
		
		clientUser = new ClientUser();
		
		String username = RLM_Parameters.getUsername();
		username = username.trim();
		if ( username != null && username.length() != 0 ) {
			window.setLoginUsername(username);
			window.setUsernameCheckBox(true);
			clientUser.setUsername(username);
		}
		
		String password = RLM_Parameters.getPassword();
		password = password.trim();
		if ( password != null && password.length() != 0 ) {
			password = rsa.decodeData(password);
			window.setLoginPassword(password);
			window.setPasswordCheckBox(true);
			clientUser.setPassword(password);
		}
		
		
		window.setInitialStatus(STATUS_NAMES[RLM_Parameters.getStatus()]);
		clientUser.setStatus(RLM_Parameters.getStatus());
		

		Color fontColor = new Color(RLM_Parameters.getFontColor_R(), RLM_Parameters.getFontColor_G(), RLM_Parameters.getFontColor_B());
		Color backgroundColor = new Color(RLM_Parameters.getBackgroundColor_R(), RLM_Parameters.getBackgroundColor_G(), RLM_Parameters.getBackgroundColor_B());
		
		window.setFontName(RLM_Parameters.getFontName());
		window.setFontStyle(RLM_Parameters.getFontStyle());
		window.setFontSize(RLM_Parameters.getFontSize());
		window.setFontColor(fontColor);
		window.setBackgroundColor(backgroundColor);
		
		clientUser.setFontName(RLM_Parameters.getFontName());
		clientUser.setFontStyle(RLM_Parameters.getFontStyle());
		clientUser.setFontSize(RLM_Parameters.getFontSize());
		clientUser.setFontColor(fontColor);
		clientUser.setBackgroundColor(backgroundColor);
	}
	
	
	/**
	 * Metoda upisuje parametre u file.
	 */
	public void writeInputParameters() {
		
		RLM_Parameters.setWidth(window.getWidth());
		RLM_Parameters.setHeight(window.getHeight());
		RLM_Parameters.setLocation_x((int)window.getLocation().getX());
		RLM_Parameters.setLocation_y((int)window.getLocation().getY());
		RLM_Parameters.setAlwaysOnTop(window.isAlwaysOnTop());
//		RLM_Parameters.setSplashScreen(window.getScreenFlag());
		
		RLM_Parameters.setUsername(clientUser != null && clientUser.getUsername() != null && window.isUsernameCheckBoxSelected() == true ? clientUser.getUsername() : "");
		RLM_Parameters.setPassword(clientUser != null && clientUser.getPassword() != null && window.isPasswordCheckBoxSelected() == true ? clientUser.getPassword() : "");
		RLM_Parameters.setStatus(clientUser.getStatus());
		RLM_Parameters.setFontName(clientUser != null ? clientUser.getFontName() : "Arial");
		RLM_Parameters.setFontStyle(clientUser != null ? clientUser.getFontStyle() : Font.PLAIN);
		RLM_Parameters.setFontSize(clientUser != null ? clientUser.getFontSize() : 12);
		Color fontColor = clientUser != null ? clientUser.getFontColor() : Color.BLACK;
		RLM_Parameters.setFontColor_R(fontColor.getRed());
		RLM_Parameters.setFontColor_G(fontColor.getGreen());
		RLM_Parameters.setFontColor_B(fontColor.getBlue());
		Color backColor = clientUser != null ? clientUser.getBackgroundColor() : Color.WHITE;
		RLM_Parameters.setBackgroundColor_R(backColor.getRed());
		RLM_Parameters.setBackgroundColor_G(backColor.getGreen());
		RLM_Parameters.setBackgroundColor_B(backColor.getBlue());
	}
	
	
	

	/**
	 * Metod vraca flag da li je pokrentu serverski proces na strani klijenta.
	 * @return - true ako jeste, inace false
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public boolean isClientServerRunning() {
		if (clientServer != null) {
			return true;
		}
		
		return false;
	}
	

	
	/**
	 * This methos creates RLM folder in user home folder and stores lock
	 * file object.
	 * 
	 * @return - true if lock acquired, otherwise false
	 */
	private boolean acquireLock() {

		lockFile = new File(RLM_Parameters.getApplicationDataFolderName(), lockFileName);
		
		try {
			if ( !lockFile.exists() ) {
				lockFile.createNewFile();
			}
			
			lock = new FileOutputStream(lockFile).getChannel().tryLock();
			
			if (lock == null) {
				return false;
			}
		}
		catch (IOException ioe) {
			ioe.printStackTrace();
		}
		return true;
	}
	
	
	
	/**
	 * This method releases acquired lock.
	 */
	private void releaseLock() {
		try {
			if (lock != null) {
				
				lock.release();
				
				lock.channel().close();
				
				if ( lockFile.exists() ) {	
					lockFile.delete();
				}
			}
		}
		catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
	
	
	
	/**
	 * Show message that RLM instance already run.
	 */
	private void alreadyRun() {
		JOptionPane.showMessageDialog(null,
				"RLM instance already started !", "Warning!",
				JOptionPane.WARNING_MESSAGE);
	}
	
	
	// shutdown hook mechanism
	public void freeResources() {
		try {
			onExitActions();
			releaseLock(); // must be the last
		}
		catch (Exception ee) {
			ee.printStackTrace();
		}
	}
	
	
	protected void finalize() throws Throwable {
		try {
			freeResources();
		}
		finally {
			super.finalize();
		}
	}
	
	
	/**
	 * This is the EXIT method of RLM Application. System.exit() activates
	 * shutdown hook mechanism.
	 */
	public static void exitRLMApplication() {
		System.exit(0);
	}
	
	
	/**
	 * Actions called on RLM exit. This method will be invoked from
	 * freeResources() method. It contains all actions that must be done before
	 * application exit.
	 */
	private void onExitActions() {
		
	}
	
	
	/**
	 * Return command line parameter value.
	 * @param param - parameter name
	 * @return parameter value
	 */
	public static String getCMDLineParameter(CmdLineParams param) {
		return cmdLineParameters.get(param.name());
	}
	
	

	/**
	 * Load RLM version from "version.dat" file.
	 */
	private void loadVersion() {
		try {
			String parent = System.getProperty("user.dir");
			File file = new File(parent + "/version.dat");
			if (!file.exists()) {
				return;
			}
			FileInputStream fis = new FileInputStream(file);
			BufferedReader br = new BufferedReader(new InputStreamReader(fis));
			version = br.readLine();
			if (version == null || version.length() == 0) {
				version = "0.0";
			}
		}
		catch (IOException ioe) {
			// ioe.printStackTrace();
			version = "0.0";
		}
	}
	
	
	/**
	 * Get RLM app. version number.
	 * 
	 * @return - version number
	 */
	public static String getVersion() {
		return version;
	}
	
	
	
	
	/**
	 * This method creates platform dependent application folder.
	 */
	private void createApplicationDataFolder() {
		File appDataFolder = new File(RLM_Parameters.getApplicationDataFolderName());
		
		if ( !appDataFolder.exists() ) {
			appDataFolder.mkdirs();
		}
	}
	
	
	
	/**
	 * Application auto-start checker.
	 * @return true, if RLM is auto-started, otherwise false
	 */
	public static boolean isAutoStarted() {
		return auto_started;
	}
	
	
	
	
	/**
	 * This method checks host platform where RLM is running.
	 */
	private static void platformChecker() {
		String osName = System.getProperty("os.name");

		// if true, Windows platform
		if ( osName.startsWith("Windows") ) {
			platform = Platform.Windows;
		}
		// otherwise, Linux or Unix platform
		else {
			platform = Platform.Non_Windows;
		}
	}
	
	
	/**
	 * Returns the platform on which RLM is started.
	 * @return Undefined, Windows or Non-Windows
	 */
	public static Platform getPlatform() {
		return platform;
	}
	
	
	
	
	/********************** >>> RLM Client Entry Point <<< **********************/

	
	/**
	 * Main metod - main entry point. **** :-) Let's Play with RAF Live Messenger :-) ****
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public static void main(String[] args) {
		
		platformChecker();
		
		for (String arg : args) {
			StringTokenizer tokens = new StringTokenizer(arg, "=");
			if ( tokens.countTokens() != 2 )
				continue;
			
			cmdLineParameters.put(tokens.nextToken(), tokens.nextToken());
		}
		
		auto_started = Boolean.parseBoolean( getCMDLineParameter(ClientApplication.CmdLineParams.auto_started) );
		
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new ClientApplication();
			}
		});
	}
	
}
